#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cassert>
#include <cstdio>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PROCESS_NUM 5

class SubEP // 把子进程和管道描述起来
{
public:
    SubEP(int writefd, pid_t pid,const std::string& name_pipe)
        : _writefd(writefd), _pid(pid),_name_pipe(name_pipe)
    {
        char tmp[1024];
        sprintf(tmp, "process_number[%d]; pid: %d; writefd：%d", _cnt++, pid, writefd);
        _name = std::string(tmp);

    }

public:
    static int _cnt;
    std::string _name;
    int _writefd;
    pid_t _pid;
    std::string _name_pipe;
};
int SubEP::_cnt = 1;

///////////////////////////////////////////////////////////模拟任务/////////////////////
typedef void (*func_t)(); // 对函数指针类型重命名为func

void func1()
{
    sleep(1);
    std::cout << "任务1：打印任务" << std::endl;
}

void func2()
{
    sleep(1);
    std::cout << "任务2：输出任务" << std::endl;
}

void func3()
{
    sleep(1);
    std::cout << "任务3：写入任务" << std::endl;
}

void func4()
{
    sleep(1);
    std::cout << "任务4：下载任务" << std::endl;
}

void func5()
{
    sleep(1);
    std::cout << "任务5：其他任务" << std::endl;
}
void LoadTask(std::vector<func_t> *funcmap) // 加载任务
{
    funcmap->push_back(func1);
    funcmap->push_back(func2);
    funcmap->push_back(func3);
    funcmap->push_back(func4);
    funcmap->push_back(func5);
}

// 创建管道
bool CreateFifo(const std::string &path)
{
    umask(0000);                        // 自己修改文件掩码
    int n = mkfifo(path.c_str(), 0666); // 如果想自己用，mode写0600；
    // mkfifo成功之后返回值为0
    // 失败就是 -1 ，并且填写对应的错误码
    if (n == 0)
    {
        return true;
    }
    else
    {
        std::cout << "error:" << strerror(errno) << std::endl;
        return false;
    }
}

// 删除管道
bool RemoveFifo(const std::string &path)
{
    // 删除此文件，
    int n = unlink(path.c_str());
    // unlink成功之后返回值为0
    // 失败就是 -1 ，并且填写对应的错误码
    if (n == 0)
    {
        return true;
    }
    else
    {
        std::cout << "error:" << strerror(errno) << std::endl;
        return false;
    }
}

void CreateSubProcess(std::vector<SubEP> *subs, std::vector<func_t> &funcmap)
{
    std::vector<int> v;
    for (int i = 1; i <= PROCESS_NUM; i++)
    {
        std::string pipe = "/home/zxf/linux_cplusplus/workdir/test_4_10/processpool2/name_pipe";
        pipe += std::to_string(i);
        CreateFifo(pipe.c_str());
        // 创建子进程
        int id = fork();
        if (id == 0)
        {
            for(int j =0; j < v.size(); j++)
            {
                close(v[j]);
            }

            int fd = open(pipe.c_str(), O_RDONLY); // 子进程只读，打开
            assert(fd > 0);
            while (true)
            {
                int code = 0;
                int n = read(fd, &code, 4);
                if (n == 4) // 读到的必须是四个字节
                {
                    funcmap[code]();
                }
                else if (n == 0) // 父进程关闭写端文件描述符，子进程读到0 ，退出。
                {
                    break;
                }
                else
                {
                    std::cout << "读取错误！！" << std::endl;
                }
            }
            exit(0);
        }
        // 父进程执行代码
        // 父进程只写打开
        int fd = open(pipe.c_str(), O_WRONLY); // 子进程只读，打开
        assert(fd > 0);
        SubEP sub(fd, id, pipe);
        subs->push_back(sub);
        v.push_back(fd);
    }
}

// 获取一个随机树
int RandomNumber(int n)
{
    return rand() % n;
}

int main()
{
    srand((unsigned long)time(NULL) ^ 12345454); // 产生随机数种子。
    // 后面的数字是为了增加随机性

    std::vector<func_t> funcmap; // 把任务组织起来
    LoadTask(&funcmap);
    // 任务全部加载到funcmap中了。

    std::vector<SubEP> subs; // 把子进程和管道组织起来
    // 建立多个子进程和对应的管道,并且传入对应的任务列表
    CreateSubProcess(&subs, funcmap);
    // 子进程和对应的管道创建完成，并且将信息保存到subs里面去了

    // 父进程开始控制子进程
    int processcnt = subs.size();
    int taskcnt = funcmap.size();
    int count = 5; // 循环次数
    // 如果大于等于0 ，如若为-1，无限执行。
    while (count)
    {
        // 选择一个子进程  //std::vector<SubEP> ->> index
        int processindex = RandomNumber(processcnt);

        // 选择一个任务   // std::vector<func_t> ->> index
        int taskindex = RandomNumber(taskcnt);

        // 把任务通过管道发送给子进程
        // 给选的任务发送四个字节的任务码
        std::cout << "send task num:" << taskindex + 1 << ' ' << "task to ->" << subs[processindex]._name << std::endl;
        int ret = write(subs[processindex]._writefd, &taskindex, 4);
        assert(ret == 4);
        (void)ret;
        sleep(2); // 每隔两秒发送一个任务码
        if (count != -1)
        {
            count--;
        }
    }



    // 回收子进程
    // 关闭写端文件描述符
    // for(int i = 0; i<subs.size(); i--)
    // {
    //     close(subs[i]._writefd);
    //     waitpid(subs[i]._pid, nullptr,0);
    //     sleep(1);
    //     std::cout<<subs[i]._name<<"--->"<<"等待完成"<<std::endl;
    // }
    for(int i = subs.size()-1; i>=0; i--)
    {
        close(subs[i]._writefd);
        waitpid(subs[i]._pid, nullptr,0);
        sleep(1);
        std::cout<<subs[i]._name<<"--->"<<"等待完成"<<std::endl;
    }


    // for(int i  =subs.size()-1; i >=0 ; i--)
    // {
    //     waitpid(subs[i]._pid, nullptr,0);
    //     sleep(1);
    //     std::cout<<subs[i]._name<<"--->"<<"等待完成"<<std::endl;
    // }


    //删除管道
    for(int i = 0; i< subs.size(); i++)
    {
        RemoveFifo(subs[i]._name_pipe);
    }

    return 0;
}